package com.zhentao.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhentao.common.Constans;
import com.zhentao.dto.ForgetPasswordDto;
import com.zhentao.dto.LoginFormDTO;
import com.zhentao.dto.UpdatePwdDto;
import com.zhentao.dto.UserDto;
import com.zhentao.mapper.UserMapper;

import com.zhentao.redis.CacheService;
import com.zhentao.service.UserService;
import com.zhentao.user.Role;
import com.zhentao.user.User;
import com.zhentao.user.UserHolder;
import com.zhentao.utils.AppHttpCodeEnum;
import com.zhentao.utils.JwtService;
import com.zhentao.utils.RegexUtils;
import com.zhentao.utils.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static com.zhentao.common.Constans.*;
import static com.zhentao.utils.AppHttpCodeEnum.USER_PHONE_CHECK;

/**
 * @author lenovo
 * @description 针对表【tb_user】的数据库操作Service实现
 * @createDate 2024-12-02 21:22:33
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private UserMapper userMapper;

    /**
     * @param user
     * @return com.zhentao.utils.ResponseResult
     * @description 用户端登录
     * @author 振涛教育_小Q
     * @date 2024/12/3 9:51
     */
    @Override
    public ResponseResult login(User user) {
        // 判断参数是否有效
        if (StringUtils.isEmpty(user)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 判断账户密码是否为空
        if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.AP_USER_DATA_NOT_EXIST);
        }
        // 验证密码是否符合标准
        if (RegexUtils.isPasswordInvalid(user.getPassword())) {
            return ResponseResult.errorResult(400, "密码不符合规则");
        }
        // 判断用户是否存在
        User user1 = getOne(Wrappers.<User>lambdaQuery()
                .eq(User::getUsername, user.getUsername()));
        if (StringUtils.isEmpty(user1)) {
            return ResponseResult.errorResult(400, "用户不存在");
        }

        // 判断密码是否正确
        if (!BCrypt.checkpw(user.getPassword(), user1.getPassword())) {
            return ResponseResult.errorResult(400, "密码不正确");
        }
        // 获取该用户对应的角色
        String token = UUID.randomUUID().toString(true);
        Role role = userMapper.selectByRoleName(user1.getRId(), user1.getPhone());
        UserDto dto = new UserDto();
        if (role != null) dto.setRoleName(role.getRoleName());
        BeanUtils.copyProperties(user1, dto);
        dto.setId(String.valueOf(user1.getId()));
        // 将实体类对象转换为hashMap结构
        Map<String, Object> map = BeanUtil.beanToMap(dto, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((firename, fileValue) -> (fileValue.toString())));
        // 存储在redis中
        String key = Constans.USER_LOGIN_REDIS + token;
        cacheService.hPutAll(key, map);
        cacheService.expire(key, Constans.USER_REDIS_EXPIRE, TimeUnit.MINUTES);
        //用户存储在用户holder中
        return ResponseResult.okResult(token);
    }

    /**
     * 发送验证码
     *
     * @return ResponseResult
     */
    @Override
    public ResponseResult sendCaptcha() {

        // 3. 符合，生成随机字符串验证码（6位，包含大小写字母和数字）
        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 6; i++) { // 修改为6位
            int index = random.nextInt(characters.length());
            builder.append(characters.charAt(index));
        }
        String code = builder.toString();
        // 4. 将验证码存储在 Redis 中
        stringRedisTemplate.opsForValue().set(USER_CAPTCHA_REDIS, code);
        stringRedisTemplate.expire(USER_CAPTCHA_REDIS, 60, TimeUnit.SECONDS);

        // 5. 发送验证码
        log.debug("发送短信验证码成功，验证码：" + code);

        // 返回
        return ResponseResult.okResult(code);
    }

    /**
     * @param loginFormDTO
     * @return com.zhentao.utils.ResponseResult
     * @description 管理端登录
     * @author 振涛教育_小Q
     * @date 2024/12/3 9:51
     */
    @Override
    public ResponseResult adminLogin(LoginFormDTO loginFormDTO) {
        // 1. 校验手机号
        String phone = loginFormDTO.getPhone();
        String code = loginFormDTO.getCode();

        log.info("登录开始，准备校验手机号为：{}", phone);
        // 校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            return ResponseResult.errorResult(USER_PHONE_CHECK);
        }

        // 判断用户是否存在
        User user1 = getOne(Wrappers.<User>lambdaQuery()
                .eq(User::getPhone, loginFormDTO.getPhone()));
        if (user1 == null) {
            return ResponseResult.errorResult(400, "用户不存在");
        }
        // 从 Redis 获取验证码并校验
        String cacheCode = stringRedisTemplate.opsForValue().get(USER_CAPTCHA_REDIS);

        if (code == null || !code.equalsIgnoreCase(cacheCode)) {
            // 验证码不一致，报错
            return ResponseResult.errorResult(400, "验证码不能为空或验证码不正确");
        }

        // 获取该用户对应的角色
        Role role = userMapper.selectByUsername(user1.getUsername());

        // 查询当前角色是否为 管理员  是 可以登录  不是不可以登录
        if (!role.getRoleName().equals("管理员")) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_ADMIND);
        }

        UserDto dto = new UserDto();
        dto.setRoleName(role.getRoleName());
        BeanUtils.copyProperties(user1, dto);

        // 将实体类对象转换为 hashMap 结构
        Map<String, Object> map = BeanUtil.beanToMap(dto, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));

        // 存储在 Redis 中
        String token = UUID.randomUUID().toString();
        String key = Constans.USER_LOGIN_REDIS + token;
        cacheService.hPutAll(key, map);
        cacheService.expire(key, Constans.USER_REDIS_EXPIRE, TimeUnit.MINUTES);
        log.info("登录成功-----------------");
        return ResponseResult.okResult(token);
    }


    /**
     * 修改密码-用户端
     *
     * @param updatePwdDto
     * @return ResponseResult
     */
    @Override
    public ResponseResult updatePassword(UpdatePwdDto updatePwdDto) {
        //1.判断前端传参是否有效
        if (StringUtils.isEmpty(updatePwdDto)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.判断参数是否为空
        if (StringUtils.isEmpty(updatePwdDto.getOldPassword()) || StringUtils.isEmpty(updatePwdDto.getNewPassword()) || StringUtils.isEmpty(updatePwdDto.getAgainNewPassword())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_NULL);
        }

        //3.获取登录用户的信息 判断旧密码是否正确
        //3.1 获取登录用户信息
        UserDto userDto = UserHolder.getUser();
        if (userDto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        User user = getOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, userDto.getUsername()));
        //3.2 匹配旧密码是否正确
        if (!BCrypt.checkpw(updatePwdDto.getOldPassword(), user.getPassword())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
        }

        //4. 判断两次输入的新密码是否一致
        if (!updatePwdDto.getNewPassword().equals(updatePwdDto.getAgainNewPassword())) {
            return ResponseResult.errorResult(500, "两次输入密码不一致 请检查后重新输入");
        }

        //5.修改密码
        user.setPassword(BCrypt.hashpw(updatePwdDto.getNewPassword()));
        updateById(user);

        //6.返回
        return ResponseResult.okResult(200, "修改密码成功");
    }


    /**
     * 忘记密码
     *
     * @param forgetPasswordDto
     * @return ResponseResult
     */
    @Override
    public ResponseResult forgetPassword(ForgetPasswordDto forgetPasswordDto) {
        //1.校验手机号
        String phone = forgetPasswordDto.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            return ResponseResult.errorResult(500, "手机号格式错误");
        }

        //2.判断前端传参是否有效
        if (StringUtils.isEmpty(forgetPasswordDto)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //3.判断参数是否为空
        if (StringUtils.isEmpty(StringUtils.isEmpty(forgetPasswordDto.getCaptcha()) || StringUtils.isEmpty(forgetPasswordDto.getNewPassword()) || StringUtils.isEmpty(forgetPasswordDto.getAgainNewPassword()))) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_NULL);
        }

        //4.判断验证码是否正确
        String captcha = stringRedisTemplate.opsForValue().get(USER_CAPTCHA_REDIS + forgetPasswordDto.getPhone());
        if (captcha == null || !captcha.equals(forgetPasswordDto.getCaptcha())) {
            return ResponseResult.errorResult(500, "验证码错误");
        }

        //5.判断两次输入的新密码是否一致
        if (!forgetPasswordDto.getNewPassword().equals(forgetPasswordDto.getAgainNewPassword())) {
            return ResponseResult.errorResult(500, "两次输入密码不一致 请检查后重新输入");
        }

        //6.修改密码
        User user = getOne(Wrappers.<User>lambdaQuery().eq(User::getPhone, phone));
        user.setPassword(BCrypt.hashpw(forgetPasswordDto.getNewPassword()));
        updateById(user);
        return ResponseResult.okResult(200, "修改密码成功");
    }

    /**
     * 获取当前登录用户
     *
     * @param
     * @return ResponseResult
     */
    @Override
    public ResponseResult userInfo() {
        UserDto user = UserHolder.getUser();
        try {
            if (StringUtils.isEmpty(user)) {
                return ResponseResult.okResult("用户信息异常");
            }
        } catch (Exception e) {
            log.error("获取用户失败：原因：{}", user);
            throw new RuntimeException(e);
        }
        return ResponseResult.okResult(user);
    }

    /**
     * 退出登录
     * @param
     * @return ResponseResult
     */
    @Override
    public ResponseResult logout(String token) {
        // 清空redis
        cacheService.delete(USER_LOGIN_REDIS+token);
        return ResponseResult.okResult("登出成功");
    }
}




